#ifndef MESH_MODEL_H
#define MESH_MODEL_H

#include <QObject>
#include <QMap>
#include <QTimer>
#include <QGradientStops>
#include <QMenu>
#include <boost/shared_ptr.hpp>
#include <util/mesh.h>

class QMenuBar;

namespace models {

class Mesh : public QObject
{
  Q_OBJECT
public:

  //
  // Mesh::Geometry
  //
  
  class Geometry {
  public:
    typedef boost::shared_ptr<Geometry> SP;
    
  public:
    Geometry(const IMesh::t_container_vertex &vertices, const QMap<IMesh::element_type, IMesh::t_container_element> &node_map);
    size_t get_vertex_qty() const;
    const IMesh::t_container_vertex  &get_vertices() const;
    const IMesh::t_container_element &get_nodes(IMesh::element_type) const;

  private:
    IMesh::t_container_vertex vertices_;
    mutable QMap<IMesh::element_type, IMesh::t_container_element> node_map_;
  };

  //
  // Mesh::Scalar_field
  //
  
  class Scalar_field {
  public:
    typedef boost::shared_ptr<Scalar_field> SP;
    typedef qreal param_t;

  public:
    Scalar_field(const QString &name, const QVector<param_t> &values);
    QString get_name() const;
    QVector<param_t> &get_values();
    const QVector<param_t> &get_values() const;
    param_t get_min() const { return min_; }
    param_t get_max() const { return max_; }
    void recalc();

  private:
    QString name_;
    QVector<param_t> values_;
    param_t min_;
    param_t max_;
  };

  //
  // Mesh::Frame
  //

  class Frame {
  public:
    typedef boost::shared_ptr<Frame> SP;
    
  public:
    Frame(Geometry::SP p_geometry, QVector<Scalar_field::SP> param);
    Geometry::SP get_geometry() const { return p_geometry_; }
    // vertex
    size_t get_param_qty() const { return param_.size(); }
    Scalar_field::SP get_param(size_t ind_param) const { return param_[ind_param]; }
    Scalar_field::SP get_param(const QString & _Name) const;
    // element
    void set_element_field(IMesh::element_type, QVector<Scalar_field::SP>);
    QVector<Scalar_field::SP> get_element_field(IMesh::element_type);
    size_t get_element_param_qty(IMesh::element_type) const;
    Scalar_field::SP get_element_param(IMesh::element_type, size_t ind_param) const;
    Scalar_field::SP get_element_param(IMesh::element_type, const QString & _Name) const;
    
  private:
    Geometry::SP p_geometry_;
    QVector<Scalar_field::SP> param_;
    QMap<IMesh::element_type, QVector<Scalar_field::SP>> element_param_;
  };

  class Frame_reader {
  public:
    typedef boost::shared_ptr<Frame_reader> SP;
  
  public:
    virtual ~Frame_reader() {}
    virtual Frame::SP read() = 0;
  };

  class Frame_random_access_reader : public Frame_reader {
  public:
    typedef boost::shared_ptr<Frame_random_access_reader> SP;

  public:
    virtual ~Frame_random_access_reader() {}
    virtual Frame::SP read() { return read(current_frame_++); }
    virtual size_t get_frame_qty() const = 0;
    virtual Frame::SP read(size_t frame) { return frame < get_frame_qty() ? do_read(frame) : Frame::SP(); }
  private:
    virtual Frame::SP do_read(size_t frame) = 0;
  private:
    size_t current_frame_;
  };

  class Frame_writer {
  public:
    virtual ~Frame_writer() {}
    virtual void write(Frame::SP p_frame) = 0;
  };
  typedef boost::shared_ptr<Frame_writer> frame_writer_ptr_t;

  class Memory_reader_writer : public Frame_random_access_reader, public Frame_writer {
  public:
    virtual ~Memory_reader_writer() {}
    virtual size_t get_frame_qty() const;
    virtual void write(Frame::SP p_frame);
  private:
    virtual Frame::SP do_read(size_t frame);
  private:
    QVector<Frame::SP> frames_;
  };

public:
  Mesh(QObject * parent = NULL);
  Mesh(Frame_reader::SP p_reader, QObject *parent = 0);
  ~Mesh();

  size_t get_frame_qty() const;
  size_t get_current_frame() const;
  void set_current_frame(size_t frame);
  QGradientStops get_gradient_stops() const;
  Frame::SP get_frame();
  size_t get_current_scalar_field() const;
signals:
  void changed();
  
public slots:
  void set_current_scalar_field(int index);
  void set_gradient_stops(const QGradientStops &);

  virtual const IMesh::t_container_vertex &get_vertices() const;
  virtual const IMesh::t_container_element &get_nodes(IMesh::element_type) const;
  virtual const IMesh::t_container_color &get_vertices_color(const QString &name) const;
  virtual const IMesh::t_container_color &get_elements_color(IMesh::element_type, const QString &name) const;

private:
  static void copy_frames(Frame_reader *p_source, Frame_writer *p_dest);
private:
  Frame_random_access_reader::SP p_frame_reader_;
  size_t frame_qty_;
  Frame::SP p_current_frame_;
  size_t scalar_field_;
  size_t current_frame_;
  QGradientStops gradient_stops_;
  QVector<IMesh::t_container_color> vertices_color_;
  QMap<IMesh::element_type, QVector<IMesh::t_container_color>> elements_color_;
  QTimer timer_;
  QMenu *p_scalar_field_menu_;

private slots:
  void on_timer();
};

} // namespace models {

#endif // MESH_MODEL_H
